from typing import Dict, List, Any, Tuple
import numpy as np
import pandas as pd
import plotly.express as px
from plotly.graph_objs import Figure
from dataclasses import dataclass
from knowledge_graph import JobKnowledgeGraph
from job_agent_system import JobAgentSystem
import textwrap
import json
from pathlib import Path

@dataclass
class Explanation:
    """推荐解释数据类"""
    job_id: str
    match_score: float
    key_reasons: List[str]
    skill_gaps: List[str]
    industry_fit: float
    experience_fit: float
    visualization: Optional[str] = None

class ExplainableAI:
    """可解释AI模块"""
    def __init__(self, kg_client: JobKnowledgeGraph, agent_system: JobAgentSystem):
        self.kg = kg_client
        self.agent_system = agent_system
        self.explanation_dir = Path("b/data/explanations")
        self.explanation_dir.mkdir(parents=True, exist_ok=True)
    
    def explain_recommendation(self, 
                             user_data: Dict[str, Any], 
                             job_data: Dict[str, Any]) -> Explanation:
        """生成推荐解释"""
        # 计算匹配分数
        match_score = self._calculate_match_score(user_data, job_data)
        
        # 生成关键原因
        key_reasons = self._generate_key_reasons(user_data, job_data)
        
        # 识别技能差距
        skill_gaps = self._identify_skill_gaps(user_data, job_data)
        
        # 计算行业匹配度
        industry_fit = self._calculate_industry_fit(user_data, job_data)
        
        # 计算经验匹配度
        experience_fit = self._calculate_experience_fit(user_data, job_data)
        
        # 创建可视化
        visualization = self._create_visualization(
            user_data, job_data, match_score
        )
        
        # 构建解释对象
        explanation = Explanation(
            job_id=job_data["job_id"],
            match_score=match_score,
            key_reasons=key_reasons,
            skill_gaps=skill_gaps,
            industry_fit=industry_fit,
            experience_fit=experience_fit,
            visualization=visualization
        )
        
        # 保存解释
        self._save_explanation(explanation)
        
        return explanation
    
    def _calculate_match_score(self, 
                             user_data: Dict[str, Any], 
                             job_data: Dict[str, Any]) -> float:
        """计算综合匹配分数"""
        # 技能匹配
        skill_score = self.agent_system.enhanced_skill_match(
            user_data["skills"],
            job_data["required_skills"]
        )
        
        # 行业匹配
        industry_score = 1.0 if user_data.get("target_industry") == job_data.get("industry") else 0.5
        
        # 经验匹配
        exp_diff = abs(user_data.get("experience", 0) - job_data.get("min_experience", 0))
        exp_score = max(0, 1 - (exp_diff / 10))  # 假设10年是最大差异
        
        # 综合分数 (加权平均)
        return 0.6 * skill_score + 0.2 * industry_score + 0.2 * exp_score
    
    def _generate_key_reasons(self,
                            user_data: Dict[str, Any],
                            job_data: Dict[str, Any]) -> List[str]:
        """生成推荐的关键原因"""
        reasons = []
        
        # 共同技能
        common_skills = set(user_data["skills"]) & set(job_data["required_skills"])
        if common_skills:
            reasons.append(
                f"您具备岗位要求的 {len(common_skills)} 项关键技能: {', '.join(common_skills)}"
            )
        
        # 行业匹配
        if user_data.get("target_industry") == job_data.get("industry"):
            reasons.append(
                f"岗位属于您的目标行业: {job_data['industry']}"
            )
        
        # 经验匹配
        if user_data.get("experience", 0) >= job_data.get("min_experience", 0):
            reasons.append(
                f"您的工作经验({user_data['experience']}年)满足岗位要求"
            )
        
        # 从知识图谱获取额外信息
        job_skills = self.kg.get_skill_graph(job_data["required_skills"][0], depth=1)
        if job_skills:
            reasons.append(
                "该岗位的核心技能在行业中需求旺盛"
            )
        
        return reasons
    
    def _identify_skill_gaps(self,
                           user_data: Dict[str, Any],
                           job_data: Dict[str, Any]) -> List[str]:
        """识别技能差距"""
        gaps = []
        required_skills = set(job_data["required_skills"])
        user_skills = set(user_data["skills"])
        
        # 缺失技能
        missing_skills = required_skills - user_skills
        if missing_skills:
            gaps.append(f"缺少技能: {', '.join(missing_skills)}")
        
        # 技能水平差距 (简化版)
        for skill in required_skills & user_skills:
            # 这里可以添加更复杂的水平评估
            gaps.append(f"建议加强 {skill} 的实战经验")
        
        return gaps
    
    def _calculate_industry_fit(self,
                             user_data: Dict[str, Any],
                             job_data: Dict[str, Any]) -> float:
        """计算行业匹配度"""
        if user_data.get("target_industry") == job_data.get("industry"):
            return 1.0
        
        # 检查行业相关性 (简化版)
        related_industries = self.kg.find_related_industries(
            user_data.get("target_industry", "")
        )
        if job_data.get("industry") in related_industries:
            return 0.7
        
        return 0.3
    
    def _calculate_experience_fit(self,
                               user_data: Dict[str, Any],
                               job_data: Dict[str, Any]) -> float:
        """计算经验匹配度"""
        user_exp = user_data.get("experience", 0)
        job_min_exp = job_data.get("min_experience", 0)
        job_max_exp = job_data.get("max_experience", 10)
        
        if job_min_exp <= user_exp <= job_max_exp:
            return 1.0
        elif user_exp > job_max_exp:
            return 0.8  # 资历过高
        else:
            return max(0, user_exp / job_min_exp)  # 线性比例
    
    def _create_visualization(self,
                           user_data: Dict[str, Any],
                           job_data: Dict[str, Any],
                           match_score: float) -> str:
        """创建解释性可视化"""
        # 技能匹配雷达图
        fig = self._create_skill_radar(user_data, job_data)
        
        # 保存图表
        chart_path = self.explanation_dir / f"chart_{job_data['job_id']}.html"
        fig.write_html(str(chart_path))
        
        return str(chart_path)
    
    def _create_skill_radar(self,
                          user_data: Dict[str, Any],
                          job_data: Dict[str, Any]) -> Figure:
        """创建技能雷达图"""
        # 准备数据
        skills = list(set(user_data["skills"] + job_data["required_skills"]))
        
        # 计算技能分数 (简化版)
        user_scores = [1 if skill in user_data["skills"] else 0 for skill in skills]
        job_scores = [1 if skill in job_data["required_skills"] else 0 for skill in skills]
        
        # 创建DataFrame
        df = pd.DataFrame({
            "skill": skills,
            "user": user_scores,
            "job": job_scores
        })
        
        # 创建雷达图
        fig = px.line_polar(
            df, 
            r="user", 
            theta="skill", 
            line_close=True,
            title="技能匹配雷达图 (蓝色: 您的技能, 红色: 岗位要求)"
        )
        
        fig.add_trace(px.line_polar(
            df, 
            r="job", 
            theta="skill", 
            line_close=True
        ).data[0])
        
        fig.update_traces(fill='toself')
        fig.update_layout(
            polar=dict(
                radialaxis=dict(
                    visible=True,
                    range=[0, 1]
                )
            ),
            showlegend=False
        )
        
        return fig
    
    def _save_explanation(self, explanation: Explanation):
        """保存解释结果"""
        data = {
            "job_id": explanation.job_id,
            "match_score": explanation.match_score,
            "key_reasons": explanation.key_reasons,
            "skill_gaps": explanation.skill_gaps,
            "industry_fit": explanation.industry_fit,
            "experience_fit": explanation.experience_fit,
            "visualization": explanation.visualization,
            "generated_at": str(datetime.now())
        }
        
        file_path = self.explanation_dir / f"explanation_{explanation.job_id}.json"
        with open(file_path, "w") as f:
            json.dump(data, f, indent=2)
    
    def generate_natural_language(self, explanation: Explanation) -> str:
        """生成自然语言解释"""
        # 构建解释文本
        reasons = "\n".join(
            f"- {reason}" for reason in explanation.key_reasons
        )
        
        gaps = "\n".join(
            f"- {gap}" for gap in explanation.skill_gaps
        ) if explanation.skill_gaps else "无显著差距"
        
        text = f"""
        推荐岗位: {explanation.job_id}
        匹配分数: {explanation.match_score:.1%}
        
        **推荐理由:**
        {reasons}
        
        **技能差距分析:**
        {gaps}
        
        **行业匹配度:** {explanation.industry_fit:.1%}
        **经验匹配度:** {explanation.experience_fit:.1%}
        
        详细可视化请查看: {explanation.visualization}
        """
        
        return textwrap.dedent(text).strip()

if __name__ == "__main__":
    # 示例使用
    from knowledge_graph import JobKnowledgeGraph
    from job_agent_system import JobAgentSystem
    
    # 初始化依赖
    kg = JobKnowledgeGraph()
    agent_system = JobAgentSystem()
    
    # 创建解释器
    explainer = ExplainableAI(kg, agent_system)
    
    # 示例数据
    user_data = {
        "user_id": "user123",
        "skills": ["Python", "SQL", "Data Analysis"],
        "experience": 3,
        "target_industry": "IT"
    }
    
    job_data = {
        "job_id": "job456",
        "title": "Data Scientist",
        "required_skills": ["Python", "Machine Learning", "Statistics"],
        "industry": "IT",
        "min_experience": 2,
        "max_experience": 5
    }
    
    # 生成解释
    explanation = explainer.explain_recommendation(user_data, job_data)
    
    # 输出自然语言解释
    print(explainer.generate_natural_language(explanation))